/**
 ******************************************************************************
 * @file    alarm_network.c
 * @author  x-itg
 * @brief   Wireless Sensor Alarm Network System - Network Protocol Implementation
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <string.h>
#include "stm32wlxx_hal.h"
#include "alarm_network.h"
#include "stm32_timer.h"
#include "utilities_def.h"
#include "radio.h"
#include "sys_app.h"
#include "node_config.h"
#include "gpio.h"

/* Global Variables */
NodeState_t State = STATE_IDLE;
Packet_t TxPacket;
Event_t events[MAX_EVENTS];
uint8_t currentEventId = 0;
uint8_t retransmitCount = 0;
uint32_t lastEventTime = 0;
uint8_t lastEventNodeId = 0;
uint32_t alarmStartTime = 0;
uint8_t currentAlarmEventId = 0;
// Calculate required ACK bitmask (10 alarm nodes: IDs 101..110)
static const uint16_t REQUIRED_ACK_MASK = ((1 << TOTAL_ALARM_NODES) - 1);

// ����ָ�룬������������״̬���涨ʱ��
void (*RestartStatusReportTimer)(void) = NULL;

/* Private Function Prototypes */
static uint32_t GetCurrentTime(void);
static void StartRetransmitTimer(void);
static void StopRetransmitTimer(void);
static void StartAlarmTimer(void);
static void StopAlarmTimer(void);

/* Timer Objects */
static UTIL_TIMER_Object_t retransmitTimer;
static UTIL_TIMER_Object_t alarmTimer;

/**
 * @brief  ACK Timer Callback - Sends acknowledgment at scheduled time slot
 */
static void OnAckTimer(void *context)
{
    if (context == NULL)
        return;

    Event_t *evt = (Event_t *)context;
    if (evt == NULL)
        return;

    // Alarm nodes send ACK when timer expires
    if (NODE_TYPE == NODE_TYPE_ALARM)
    {
        SendAckPacket(evt->event_id);
        evt->ack_sent = 1; /* mark that this alarm node has sent ACK for this event */
        //APP_LOG(TS_ON, VLEVEL_M, "[MM] [ND:%03d][EV:%04d] Scheduled ACK transmission\r\n",NODE_ID, evt->event_id);
    }
}

/**
 * @brief  Retransmission Timer Callback
 */
static void OnRetransmitTimer(void *context)
{
    if (State == STATE_WAITING_ACK)
    {
        HandleRetransmission();
    }
}

/**
 * @brief  Alarm Timeout Timer Callback
 */
static void OnAlarmTimer(void *context)
{
    HandleAlarmTimeout();
}

/**
 * @brief  System Initialization
 */
void AlarmNetwork_Init(void)
{
    // Initialize events array
    memset(events, 0, sizeof(events));

    // Initialize retransmission timer
    UTIL_TIMER_Create(&retransmitTimer, RETRANSMIT_TIMEOUT_MS,
                      UTIL_TIMER_ONESHOT, OnRetransmitTimer, NULL);

    // Initialize alarm timer
    UTIL_TIMER_Create(&alarmTimer, ALARM_DURATION_MS,
                      UTIL_TIMER_ONESHOT, OnAlarmTimer, NULL);

    APP_LOG(TS_ON, VLEVEL_M, "[CM][ND:%03d] Node Type: %s\r\n",
            NODE_ID, (NODE_TYPE == NODE_TYPE_COLLECTOR) ? "Collector" : "Alarm");
}

/**
 * @brief  Calculate Packet Checksum
 */
uint8_t CalculateChecksum(Packet_t *packet)
{
    uint8_t checksum = 0;
    uint8_t *data = (uint8_t *)packet;

    for (int i = 0; i < 5; i++)
    {
        checksum += data[i];
    }

    return checksum;
}

/**
 * @brief  Validate Packet Integrity
 */
uint8_t ValidatePacket(Packet_t *packet)
{
    if (packet->preamble != 0xAA)
    {
        APP_LOG(TS_ON, VLEVEL_H, "[CC] Invalid preamble: 0x%02X\r\n", packet->preamble);
        return 0;
    }

    if (packet->node_type != NODE_TYPE_COLLECTOR &&
        packet->node_type != NODE_TYPE_ALARM)
    {
        APP_LOG(TS_ON, VLEVEL_H, "[CC] Invalid node type: 0x%02X\r\n", packet->node_type);
        return 0;
    }

    if (packet->node_type == NODE_TYPE_COLLECTOR)
    {
        if (packet->node_id < COLLECTOR_MIN_ID || packet->node_id > COLLECTOR_MAX_ID)
        {
            APP_LOG(TS_ON, VLEVEL_H, "[CC] Invalid collector ID: %d\r\n", packet->node_id);
            return 0;
        }
    }
    else
    {
        if (packet->node_id < ALARM_MIN_ID || packet->node_id > ALARM_MAX_ID)
        {
            APP_LOG(TS_ON, VLEVEL_H, "[CC] Invalid alarm node ID: %d\r\n", packet->node_id);
            return 0;
        }
    }

    uint8_t calculated_checksum = CalculateChecksum(packet);
    if (packet->checksum != calculated_checksum)
    {
        APP_LOG(TS_ON, VLEVEL_H, "[CC] Checksum error: Expected 0x%02X, Received 0x%02X\r\n",
                calculated_checksum, packet->checksum);
        return 0;
    }

    return 1;
}

/**
 * @brief  Find Existing Event
 */
uint8_t FindEvent(uint8_t node_id, uint32_t current_time)
{
    for (int i = 0; i < MAX_EVENTS; i++)
    {
        if (events[i].node_id == node_id &&
            events[i].event_id != 0 &&
            (current_time - events[i].timestamp) <= EVENT_TIMEOUT_MS)
        {
            return events[i].event_id;
        }
    }
    return 0;
}

/**
 * @brief  Create New Event
 */
uint8_t CreateNewEvent(uint8_t node_id, uint32_t current_time)
{
    // Find available event slot
    for (int i = 0; i < MAX_EVENTS; i++)
    {
        if (events[i].event_id == 0)
        {
            events[i].node_id = node_id;
            events[i].timestamp = current_time;
            events[i].event_id = ++currentEventId;
            events[i].confirmed = 0;
            events[i].ack_received_count = 0;
            events[i].ack_bitmap = 0;
            events[i].ack_sent = 0; // Initialize ack_sent
            // Initialize per-event ackTimer object
            UTIL_TIMER_Create(&events[i].ackTimer, RETRANSMIT_TIMEOUT_MS, UTIL_TIMER_ONESHOT, OnAckTimer, &events[i]);
#ifdef DEVICE_TYPE_IS_COLLECTOR//collector
            APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d][Created EVENT]\r\n",node_id, currentEventId);
#else//alarm
            APP_LOG(TS_ON, VLEVEL_M, "[MM] [ND:%03d][EV:%04d][Created EVENT]\r\n",node_id, currentEventId);
#endif
            return currentEventId;
        }
    }

    // Overwrite oldest event if no slots available
    uint32_t oldest_time = 0xFFFFFFFF;
    uint8_t oldest_index = 0;

    for (int i = 0; i < MAX_EVENTS; i++)
    {
        if (events[i].timestamp < oldest_time)
        {
            oldest_time = events[i].timestamp;
            oldest_index = i;
        }
    }

    events[oldest_index].confirmed = 0;
    events[oldest_index].ack_received_count = 0;
    events[oldest_index].ack_bitmap = 0;
    events[oldest_index].ack_sent = 0; // Initialize ack_sent
    events[oldest_index].confirmed = 0;
    events[oldest_index].ack_received_count = 0;
    events[oldest_index].ack_bitmap = 0;
    UTIL_TIMER_Stop(&events[oldest_index].ackTimer);
    UTIL_TIMER_Create(&events[oldest_index].ackTimer, RETRANSMIT_TIMEOUT_MS, UTIL_TIMER_ONESHOT, OnAckTimer, &events[oldest_index]);

    APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d] Replaced Old Event\r\n",
            node_id, currentEventId);
    return currentEventId;
}

/**
 * @brief  Process Alarm Packet
 */
void ProcessAlarmPacket(Packet_t *packet)
{
    if (packet->node_type != NODE_TYPE_COLLECTOR)
    {
        return;
    }

    uint32_t current_time = GetCurrentTime();

    // Find or create event
    uint8_t event_id = FindEvent(packet->node_id, current_time);
    if (event_id == 0)
    {
        event_id = CreateNewEvent(packet->node_id, current_time);
    }

    // Alarm nodes trigger alarm and send ACK
    if (NODE_TYPE == NODE_TYPE_ALARM)
    {
        // Activate GPIOB2 alarm output
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
        alarmStartTime = current_time;
        currentAlarmEventId = event_id;

        // Start alarm timer
        StartAlarmTimer();

        // Schedule ACK transmission with time slot delay to avoid collisions
        uint32_t slot_ms = RETRANSMIT_TIMEOUT_MS / (TOTAL_ALARM_NODES + 1);
        uint8_t slot_index = 0;
        if (NODE_ID >= ALARM_MIN_ID && NODE_ID <= ALARM_MAX_ID)
        {
            slot_index = NODE_ID - ALARM_MIN_ID; // 0..(TOTAL_ALARM_NODES-1)
        }
        uint32_t delay_ms = (uint32_t)(slot_index + 1) * slot_ms; // Start from 1 for boundary

        // Use per-event ackTimer with calculated delay
        for (int i = 0; i < MAX_EVENTS; i++)
        {
            if (events[i].event_id == event_id)
            {
                // If this alarm node has already sent ACK for this event, skip scheduling
                if (events[i].ack_sent)
                {
                    //APP_LOG(TS_ON, VLEVEL_M, "[MM] [ND:%03d][EV:%04d][ACK already sent, skip schedule GPIO(01)]\r\n",packet->node_id, event_id);
                    break;
                }

                UTIL_TIMER_Stop(&events[i].ackTimer);
                UTIL_TIMER_Create(&events[i].ackTimer, delay_ms, UTIL_TIMER_ONESHOT, OnAckTimer, &events[i]);
                UTIL_TIMER_Start(&events[i].ackTimer);
                //APP_LOG(TS_ON, VLEVEL_M, "[CC] [AlarmNode:%03d][EV:%04d], ACK scheduled at %dms (slot %d)\r\n",packet->node_id, event_id, delay_ms, slot_index);
                break;
            }
        }

        //APP_LOG(TS_ON, VLEVEL_M, "[CC] [AlarmNode:%03d][EV:%04d]\r\n",packet->node_id, event_id);
    }
}

/**
 * @brief  Process Acknowledgment Packet
 */
void ProcessAckPacket(Packet_t *packet)
{
    // Only collectors process ACK packets
    if (NODE_TYPE != NODE_TYPE_COLLECTOR)
    {
        return;
    }

    if (packet->node_type != NODE_TYPE_ALARM)
    {
        return;
    }

    // Find corresponding event and record ACK source
    for (int i = 0; i < MAX_EVENTS; i++)
    {
        if (events[i].event_id == packet->event_id)
        {
            // Calculate bit position in bitmap (ID 101 -> bit0)
            if (packet->node_id >= ALARM_MIN_ID && packet->node_id <= ALARM_MAX_ID)
            {
                uint8_t bit_index = packet->node_id - ALARM_MIN_ID; // 0..9
                events[i].ack_bitmap |= (1 << bit_index);
                // Update ACK received count
                uint8_t count = 0;
                uint16_t mask = events[i].ack_bitmap;
                for (int b = 0; b < TOTAL_ALARM_NODES; b++)
                {
                    if (mask & (1 << b))
                        count++;
                }
                events[i].ack_received_count = count;

                APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d] form ACK Received: (%d/%d)\r\n",
                        packet->node_id,
                        packet->event_id,
                        events[i].ack_received_count,
                        TOTAL_ALARM_NODES);

                // Event fully confirmed when all alarm nodes have ACKed
                if ((events[i].ack_bitmap & REQUIRED_ACK_MASK) == REQUIRED_ACK_MASK)
                {
                    events[i].confirmed = 1;
                    StopRetransmitTimer();
                    UTIL_TIMER_Stop(&events[i].ackTimer);
                    State = STATE_IDLE;
                    retransmitCount = 0;
                    // Reset last event time to allow new alarms after 5 seconds
                    lastEventTime = 0;
                    lastEventNodeId = 0;
                    currentAlarmEventId = 0;

                    if (RestartStatusReportTimer != NULL)
                    {
                        RestartStatusReportTimer();
                    }
                    APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d] Event Fully Confirmed: (%d/%d)\r\n",
                            packet->node_id,
                            packet->event_id,
                            events[i].ack_received_count,
                            TOTAL_ALARM_NODES);
                }
            }
            break;
        }
    }
}

/**
 * @brief  Send Alarm Packet
 */
void SendAlarmPacket(uint8_t event_id)
{
    TxPacket.preamble = 0xAA;
    TxPacket.node_type = NODE_TYPE_COLLECTOR;
    TxPacket.node_id = NODE_ID;
    TxPacket.event_id = event_id;
    TxPacket.data = 1; // Alarm state
    TxPacket.checksum = CalculateChecksum(&TxPacket);

    State = STATE_SENDING;
    Radio.Send((uint8_t *)&TxPacket, sizeof(TxPacket));

    APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d][Send Alarm Packet:%02X]\r\n",
            TxPacket.node_id,
            TxPacket.event_id,
            TxPacket.data);
}

/**
 * @brief  Send Acknowledgment Packet
 */
void SendAckPacket(uint8_t event_id)
{
    TxPacket.preamble = 0xAA;
    TxPacket.node_type = NODE_TYPE_ALARM;
    TxPacket.node_id = NODE_ID;
    TxPacket.event_id = event_id;
    TxPacket.data = 0; // ACK state
    TxPacket.checksum = CalculateChecksum(&TxPacket);

    State = STATE_SENDING;
    Radio.Send((uint8_t *)&TxPacket, sizeof(TxPacket));
    APP_LOG(TS_ON, VLEVEL_M, "[MM] [ND:%03d][EV:%04d][Send ACK Packet:(%02X)]\r\n",
            TxPacket.node_id,
            TxPacket.event_id,
            TxPacket.data);
}

/**
 * @brief  Handle Packet Retransmission
 */
void HandleRetransmission(void)
{
    if (State != STATE_WAITING_ACK)
    {
        return;
    }

    // Find current event slot
    for (int i = 0; i < MAX_EVENTS; i++)
    {
        if (events[i].event_id == currentAlarmEventId)
        {
            APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d][ACKBITMAP:0x%04X][REQUIRED_ACK_MASK:0x%04X] Find Event ID\r\n",
                    NODE_ID,
                    events[i].event_id,
                    events[i].ack_bitmap,
                    REQUIRED_ACK_MASK);

            // Skip retransmission if all ACKs received
            if ((events[i].ack_bitmap & REQUIRED_ACK_MASK) == REQUIRED_ACK_MASK)
            {
                APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d][ACKBITMAP:0x%04X][REQUIRED_ACK_MASK:0x%04X] All ACKs Received\r\n",
                        NODE_ID,
                        events[i].event_id,
                        events[i].ack_bitmap,
                        REQUIRED_ACK_MASK);
                events[i].confirmed = 1;
                State = STATE_IDLE;
                retransmitCount = 0;
                StopRetransmitTimer();
                // Reset last event time to allow new alarms after 5 seconds
                lastEventTime = 0;
                lastEventNodeId = 0;
                currentAlarmEventId = 0;
                return;
            }

            if (retransmitCount < MAX_RETRANSMIT_COUNT)
            {
                retransmitCount++;
                // Retransmit alarm packet
                SendAlarmPacket(currentAlarmEventId);
                // Important: Set state back to WAITING_ACK after sending
                State = STATE_WAITING_ACK;
                // Restart retransmission timer for exactly 1 second
                StopRetransmitTimer();
                UTIL_TIMER_Create(&retransmitTimer, RETRANSMIT_TIMEOUT_MS,
                                  UTIL_TIMER_ONESHOT, OnRetransmitTimer, NULL);
                UTIL_TIMER_Start(&retransmitTimer);

                APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d] (Received %d/%d Re:%d)\r\n",
                        NODE_ID,
                        currentAlarmEventId,
                        events[i].ack_received_count,
                        TOTAL_ALARM_NODES,
                        retransmitCount);
            }
            else
            {
                // Maximum retransmission attempts reached
                APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d] (Received %d/%d La:%d)\r\n",
                        NODE_ID,
                        currentAlarmEventId,
                        events[i].ack_received_count,
                        TOTAL_ALARM_NODES,
                        retransmitCount);
                UTIL_TIMER_Stop(&events[i].ackTimer);
                State = STATE_IDLE;
                retransmitCount = 0;
                StopRetransmitTimer();
                // Reset last event time to allow new alarms after 5 seconds
                lastEventTime = 0;
                lastEventNodeId = 0;
                currentAlarmEventId = 0;

                if (RestartStatusReportTimer != NULL)
                {
                    RestartStatusReportTimer();
                }
            }
            break; 
        }
    }
}

/**
 * @brief  Handle Alarm Timeout
 */
void HandleAlarmTimeout(void)
{
    if (NODE_TYPE == NODE_TYPE_ALARM)
    {
        // Deactivate alarm output Reset GPIO
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
        APP_LOG(TS_ON, VLEVEL_M, "[MM] [ND:%03d][EV:%04d][Alarm timeout deactivated GPIO(00)]\r\n", NODE_ID, currentAlarmEventId);
        //开启接收
        Radio.Rx(RX_TIMEOUT_VALUE);
    }
}

/**
 * @brief  Process Sensor Data
 */
void ProcessSensorData(uint8_t sensor_id, uint8_t sensor_value)
{
    if (NODE_TYPE != NODE_TYPE_COLLECTOR)
    {
        return;
    }

    if (sensor_value == 1)
    { // Alarm triggered
        uint32_t current_time = GetCurrentTime();

        // Check for duplicate alarm from same device within 5 seconds
        if (lastEventNodeId == NODE_ID &&
            (current_time - lastEventTime) <= EVENT_TIMEOUT_MS)
        {
            // Use same event ID - but check if we're still in retransmission phase
            currentAlarmEventId = FindEvent(NODE_ID, current_time);

            // Only process if we're not currently waiting for ACKs
            if (State != STATE_WAITING_ACK)
            {
                // Send alarm packet for duplicate event
                SendAlarmPacket(currentAlarmEventId);
                // Start retransmission timer
                State = STATE_WAITING_ACK;
                retransmitCount = 0;
                StartRetransmitTimer();
            }

            return;
        }
        else
        {
            // Create new event
            currentAlarmEventId = CreateNewEvent(NODE_ID, current_time);
            lastEventTime = current_time;
            lastEventNodeId = NODE_ID;
        }

        // Send alarm packet
        SendAlarmPacket(currentAlarmEventId);

        // Start retransmission timer
        State = STATE_WAITING_ACK;
        retransmitCount = 0;
        StartRetransmitTimer();

        APP_LOG(TS_ON, VLEVEL_M, "[CC] [ND:%03d][EV:%04d][SE:%d][+++ Triggered Alarm +++]\r\n",
                NODE_ID, currentAlarmEventId, sensor_id, currentAlarmEventId);
    }
}

/**
 * @brief  Radio Packet Reception Handler
 */
void RadioPacketHandler(uint8_t *payload, uint16_t size)
{
    if (size == sizeof(Packet_t))
    {
        Packet_t *packet = (Packet_t *)payload;

        if (ValidatePacket(packet))
        {
            /* Ignore self-sent packets (radio loopback/self-echo) */
            if (packet->node_id == NODE_ID)
            {
                APP_LOG(TS_ON, VLEVEL_M, "Ignored self-sent packet: node_id=%d event_id=%d\r\n", packet->node_id, packet->event_id);
                return;
            }
            if (packet->data == 1)
            { // Alarm packet
                ProcessAlarmPacket(packet);
            }
            else
            { // ACK packet
                ProcessAckPacket(packet);
            }
        }
    }
}

/* Private Function Implementations */
static uint32_t GetCurrentTime(void)
{
    return HAL_GetTick();
}

static void StartRetransmitTimer(void)
{
    StopRetransmitTimer(); // Ensure timer is stopped first
    UTIL_TIMER_Create(&retransmitTimer, RETRANSMIT_TIMEOUT_MS,
                      UTIL_TIMER_ONESHOT, OnRetransmitTimer, NULL);
    UTIL_TIMER_Start(&retransmitTimer);
}

static void StopRetransmitTimer(void)
{
    UTIL_TIMER_Stop(&retransmitTimer);
}

static void StartAlarmTimer(void)
{
    UTIL_TIMER_Start(&alarmTimer);
}

static void StopAlarmTimer(void)
{
    UTIL_TIMER_Stop(&alarmTimer);
}